package com.sharp.socket.io.config;

import com.corundumstudio.socketio.*;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.sharp.framework.aop.publish.RedisPublishConfig;
import com.sharp.framework.utils.NetUtil;
import com.sharp.kernel.utils.ReflectionUtil;
import com.sharp.socket.io.dao.mongo.po.TopicChatMessage;
import com.sharp.socket.io.service.LiveReceiveMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * Title: BaseLiveSocketIoConfig
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2020/9/22 15:42
 * @description Project Name: customer-robot
 * @Package: com.bhfae.customer.robot.config.socket
 */
@Slf4j
public abstract class BaseLiveSocketIoConfig implements ApplicationContextAware, CommandLineRunner {

    public static final String TOKEN = "token";
    public static final String OPERATOR = "operator";
    public static final String ROOM_ID = "roomId";
    private static final String PROJECT_NAME_PROPERTY = "my.project.name";

    protected static ApplicationContext applicationContextInstance;
    @Resource
    private RedisPublishConfig redisPublishConfig;

    @Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
        return new SpringAnnotationScanner(socketServer);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContextInstance = applicationContext;
    }

    @Override
    public void run(String... args) {
        Object delegate = applicationContextInstance.getBean(LiveReceiveMessageService.class);
        Method msgMethod = ReflectionUtil.getMethod(LiveReceiveMessageService.class, "receiveSubscribeMessage", TopicChatMessage.class);
        redisPublishConfig.registerPublish(buildChatMessageTopic(), delegate, msgMethod, TopicChatMessage.class);
        log.info("socket io 服务启动");
        socketIOServer().start();
    }

    public static String buildChatMessageTopic() {
        return applicationContextInstance.getEnvironment().getProperty(PROJECT_NAME_PROPERTY) + "-" + SocketIoRedisKey.SOCKET_IO_MSG_TOPIC_KEY;
    }

    @Bean
    public SocketIOServer socketIOServer() {
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setReuseAddress(true);
        Configuration config = new Configuration();
        String localIp = NetUtil.getLocalIp();
        config.setHostname(localIp);
        config.setPort(port());
        config.setSocketConfig(socketConfig);
        config.setExceptionListener(new SockIoExceptionListener());
        config.setRandomSession(true);

        //该处可以用来进行身份验证
        config.setAuthorizationListener(onAuthorize());

        SocketIOServer socketIOServer = new SocketIOServer(config);
        socketIOServer.addConnectListener(onConnect());
        socketIOServer.addDisconnectListener(onDisconnected());
        return socketIOServer;

    }

    /**
     * 设置端口号
     *
     * @return
     */
    protected abstract int port();

    /**
     * 鉴权回调
     *
     * @return
     */
    protected abstract AuthorizationListener onAuthorize();

    /**
     * 连接回调
     *
     * @return
     */
    protected abstract ConnectListener onConnect();

    /**
     * 断开回调
     *
     * @return
     */
    protected abstract DisconnectListener onDisconnected();

    /**
     * 配置消息处理
     *
     * @param socketIOServer    server
     * @param socketIONamespace namespace
     */
    protected abstract void addEventDataListener(SocketIOServer socketIOServer, SocketIONamespace socketIONamespace);

    protected void baseOnConnect(SocketIOClient client) {
        Object delegate = applicationContextInstance.getBean(SocketIoEndpoint.class);
        Method method = ReflectionUtil.getMethod(SocketIoEndpoint.class, "onConnect", SocketIOClient.class);
        ReflectionUtil.invoke(delegate, method, client);
    }

    protected void baseOnDisconnect(SocketIOClient client) {
        Object delegate = applicationContextInstance.getBean(SocketIoEndpoint.class);
        Method method = ReflectionUtil.getMethod(SocketIoEndpoint.class, "onDisconnect", SocketIOClient.class);
        ReflectionUtil.invoke(delegate, method, client);
    }


}
